package com.hyt.it.ogt.kq.service.gov.service.impl;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.handler.AbstractRowWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.core.base.BaseEntity;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.constant.CommonConstant;
import com.hyt.core.exception.NestedBusinessException;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.enums.SelfArrangeEnum;
import com.hyt.it.ogt.kq.common.exception.GovInvalidRequestException;
import com.hyt.it.ogt.kq.common.gov.constant.CodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.constant.StringCodeEnum;
import com.hyt.it.ogt.kq.common.gov.secmodule.SecModuleUtil;
import com.hyt.it.ogt.kq.common.gov.utils.TextUtil;
import com.hyt.it.ogt.kq.common.utils.TreeToListConverter;
import com.hyt.it.ogt.kq.service.gov.feign.pt.GovPtClient;
import com.hyt.it.ogt.kq.service.gov.mapper.TaskRoomMapper;
import com.hyt.it.ogt.kq.service.gov.mapper.TimeRoomCandidateMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.AreaDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.ExportPlaceRoomDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.ProgressResult;
import com.hyt.it.ogt.kq.service.gov.model.dto.TaskTimeRoomArrangeDTO;
import com.hyt.it.ogt.kq.service.gov.model.entity.OfficeArea;
import com.hyt.it.ogt.kq.service.gov.model.entity.OfficePlace;
import com.hyt.it.ogt.kq.service.gov.model.entity.OfficeRoom;
import com.hyt.it.ogt.kq.service.gov.model.entity.TaskArea;
import com.hyt.it.ogt.kq.service.gov.model.entity.TaskPlace;
import com.hyt.it.ogt.kq.service.gov.model.entity.TaskRoom;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeRoomCandidate;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelLineErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.ExcelRowErr;
import com.hyt.it.ogt.kq.service.gov.model.excel.ImportPlaceRoomSheet;
import com.hyt.it.ogt.kq.service.gov.model.excel.ImportSelfAreaSheet;
import com.hyt.it.ogt.kq.service.gov.model.excel.OfficeAreaSheet;
import com.hyt.it.ogt.kq.service.gov.model.excel.listener.PlaceRoomListener;
import com.hyt.it.ogt.kq.service.gov.model.excel.listener.SelfLevelListener;
import com.hyt.it.ogt.kq.service.gov.model.json.examsys.obt.RoomInfoResult;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskRoomQueryResultVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskRoomTotalVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskTimeRoomArrangeOrNoVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.arrange.ArrangeCount;
import com.hyt.it.ogt.kq.service.gov.model.vo.arrange.ArrangeRoomVO;
import com.hyt.it.ogt.kq.service.gov.model.xml.examsys.gce.RegisterInfoGceResult;
import com.hyt.it.ogt.kq.service.gov.model.xml.examsys.obt.RegisterInfoObtResult;
import com.hyt.it.ogt.kq.service.gov.service.IArrangeService;
import com.hyt.it.ogt.kq.service.gov.service.IOfficeAreaService;
import com.hyt.it.ogt.kq.service.gov.service.IOfficePlaceService;
import com.hyt.it.ogt.kq.service.gov.service.IOfficeRoomService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskAreaService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskPlaceService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskRoomService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeRoomService;
import com.hyt.it.ogt.kq.service.gov.service.obs.CloudObsService;
import com.hyt.log.service.IUserInfoService;
import com.hyt.loginfo.model.DeptVO;
import com.hyt.loginfo.model.Organization;
import com.hyt.loginfo.model.Tree;
import com.hyt.loginfo.model.User;
import com.hyt.loginfo.model.UserOrgAccessDTO;
import com.hyt.loginfo.model.param.QueryOrgsParam;
import com.hyt.loginfo.service.IUaDeptAccessService;
import com.hyt.loginfo.service.IUaDeptService;
import com.hyt.progress.service.ProgressManager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 任务考场表 服务实现类
 * </p>
 *
 * @author huangyh
 * @since 2021-04-12
 */
@Slf4j
@Service
public class TaskRoomServiceImpl extends BaseServiceImpl<TaskRoomMapper, TaskRoom> implements ITaskRoomService {

    @Autowired
    private ITaskPlaceService iTaskPlaceService;
    @Resource
    private ITaskService iTaskService;
    @Resource
    private ITaskAreaService iTaskAreaService;
    @Resource
    private IUaDeptService iUaDeptService;
    @Resource
    private TaskRoomMapper taskRoomMapper;

    @Resource
    private IOfficePlaceService iOfficePlaceService;

    @Resource
    private IOfficeRoomService iOfficeRoomService;
    @Resource
    private ITimeRoomService iTimeRoomService;

    @Resource
    private GovPtClient govPtClient;
    @Resource
    private CloudObsService cloudObsService;
    @Resource
    private TimeRoomCandidateMapper timeRoomCandidateMapper;
    @Resource
    private IOfficeAreaService iOfficeAreaService;
    @Resource
    private IUserInfoService iUserInfoService;
    @Resource
    private IUaDeptAccessService iUaDeptAccessService;
 	@Resource
    private IArrangeService iArrangeService;


    private void check(String taskId) {
        iTaskService.isLockDataThrowException(taskId);
        //线上模式，虚拟考场，不允许添加编排考点考场
        if (!iTimeRoomService.whetherEditTimeRoom(taskId)) {
            throw new KqException(ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_EDIT.getCode(), ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_EDIT.getMsg());
        }
        // 如果被绑定则不让删除
        int count = timeRoomCandidateMapper.selectCount(Wrappers.<TimeRoomCandidate>lambdaQuery().eq(TimeRoomCandidate::getTaskId, taskId).eq(BaseEntity::getDelFlag, false));
        if(count > 0) {
            throw new KqException(ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getCode(), ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getMsg());
        }
    }

    private void deleteCheck(String taskId) {
        iTaskService.isLockDataThrowException(taskId);
        //线上模式，虚拟考场，不允许添加编排考点考场
        if (!iTimeRoomService.whetherEditTimeRoom(taskId)) {
            throw new KqException(ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_DELETE.getCode(), ResponseCode.TIMES_ONLINE_TIME_ROOM_NOT_DELETE.getMsg());
        }
        // 如果被绑定则不让删除
        int count = timeRoomCandidateMapper.selectCount(Wrappers.<TimeRoomCandidate>lambdaQuery().eq(TimeRoomCandidate::getTaskId, taskId).eq(BaseEntity::getDelFlag, false));
        if(count > 0) {
            throw new KqException(ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getCode(), ResponseCode.ERROR_OVERVIEW_HAS_ARRANGED.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = NestedBusinessException.class)
    public void addArrangeRoom(String roomIds, String officeId, String taskId) {
        this.check(taskId);
        //转换考场id
        List<String> roomIdList = Arrays.asList(roomIds.split(","));
        //获取机构考场模板数据
        List<OfficeRoom> officeRoomList = this.getOfficeRoom(roomIdList);
        //获取机构考点模板数据
        List<OfficePlace> officePlaceList = this.getOfficePlace(officeRoomList);
        //根据机构考点模板数据获取机构区域模板数据
        List<OfficeArea> officeAreaList = this.getOfficeArea(officePlaceList, officeId);
        //处理并插入任务编排考点考场数据
        this.insertArrangeRoom(taskId, officeRoomList, officePlaceList, officeAreaList);
    }

    /**
     * 处理并插入任务编排考点考场数据
     * @param taskId 考试任务id
     * @param officeRoomList 机构考场数据
     * @param officePlaceList 机构考点数据
     * @param officeAreaList 机构区域数据
     * @throws GovInvalidRequestException 非法请求，抛出异常
     * @throws NestedBusinessException 插入失败，抛出异常
     */
    private void insertArrangeRoom(String taskId, List<OfficeRoom> officeRoomList,
                                   List<OfficePlace> officePlaceList,
                                   List<OfficeArea> officeAreaList) throws GovInvalidRequestException, NestedBusinessException {
        //根据考试任务id获取当前任务编排考点数据
        QueryWrapper<TaskPlace> taskPlaceQueryWrapper = new QueryWrapper<>();
        taskPlaceQueryWrapper.lambda()
                .eq(TaskPlace::getTaskId, taskId)
                .eq(TaskPlace::getDelFlag, false);
        List<TaskPlace> currentTaskPlaceList = iTaskPlaceService.list(taskPlaceQueryWrapper);
        //考场编号获取考点的id
        Map<Integer,String> taskCodeIdMap = new HashMap<>(2);
        //机构考点Id获取code
        Map<String,Integer> officeIdCodeMap = new HashMap<>(2);
        if (!CollectionUtils.isEmpty(currentTaskPlaceList)) {
            currentTaskPlaceList.stream().forEach(taskPlace -> {
                taskCodeIdMap.put(taskPlace.getCode(),taskPlace.getId());
            });
            officePlaceList.stream().forEach(officePlace -> {
                officeIdCodeMap.put(officePlace.getId(),officePlace.getCode());
            });
            //去掉重复的编排考点数据，留下新增的编排考点数据
            List<String> taskPlaceCodeList = currentTaskPlaceList.stream()
                    //.map(TaskPlace::getCode)
                    .filter(t -> Objects.nonNull(t.getPtPlaceId()))
                    .map(TaskPlace::getPtPlaceId)
                    .collect(Collectors.toList());
            officePlaceList = officePlaceList.stream()
                    .filter(officePlace -> taskPlaceCodeList.stream()
                            //.noneMatch(taskPlaceCode -> taskPlaceCode.equals(officePlace.getCode())))
                            .noneMatch(taskPlaceCode -> taskPlaceCode.equals(officePlace.getId())))
                    .collect(Collectors.toList());
        }
        //生成考试任务编排考点数据
        List<TaskPlace> taskPlaceList = new ArrayList<>();

        officePlaceList.forEach(officePlace -> {
            TaskPlace taskPlace = new TaskPlace();
            BeanUtils.copyProperties(officePlace, taskPlace);
            String placeId = UUIDUtils.newSortUUID();
            taskPlace.setTaskId(taskId).setPtPlaceId(officePlace.getId())
                    .setRegisterCode(SecModuleUtil.encrypStringNew(String.valueOf(taskPlace.getCode())))
                    .setId(placeId);
            officeIdCodeMap.put(officePlace.getId(),officePlace.getCode());

            taskCodeIdMap.put(taskPlace.getCode(),placeId);
            taskPlaceList.add(taskPlace);
        });

        //根据考试任务id获取当前任务编排考场数据
        QueryWrapper<TaskRoom> taskRoomQueryWrapper = new QueryWrapper<>();
        taskRoomQueryWrapper.lambda()
                .eq(TaskRoom::getTaskId, taskId)
                .eq(TaskRoom::getDelFlag, false);
        List<TaskRoom> currentTaskRoomList = list(taskRoomQueryWrapper);
        if (!CollectionUtils.isEmpty(currentTaskRoomList)) {
            //校验编排考场是否重复添加
            List<String> taskRoomCodeList = currentTaskRoomList.stream()
                    .map(TaskRoom::getPtRoomId)
                    .collect(Collectors.toList());
            officeRoomList.forEach(officeRoom -> {
                String officeRoomCode = officeRoom.getId();
                boolean repeat = taskRoomCodeList.stream()
                        .anyMatch(officeRoomCode::equals);
                if (repeat) {
                    //添加的编排考场中存在与当前任务已添加的考场重复的考场，请勿重复添加
                    throw new KqException(ResponseCode.ROOM_ADD_EXIST.getCode(),
                            ResponseCode.ROOM_ADD_EXIST.getMsg());
                }
            });
        }
        //生成考试任务编排考场数据
        List<TaskRoom> taskRoomList = new ArrayList<>();
        officeRoomList.forEach(officeRoom -> {
            TaskRoom taskRoom = new TaskRoom();
            BeanUtils.copyProperties(officeRoom, taskRoom);
            int placeCode = officeIdCodeMap.get(officeRoom.getPlaceId());
            taskRoom.setTaskId(taskId)
                    .setRegisterCode(SecModuleUtil.encrypStringNew(String.valueOf(taskRoom.getCode())))
                    .setPlaceId(taskCodeIdMap.get(placeCode))
                    .setRegistered(false)
                    .setClosure(false)
                    .setReserve(false)
                    .setCode(officeRoom.getCode())
                    .setId(UUIDUtils.newSortUUID());
            taskRoom.setPtRoomId(officeRoom.getId());
            taskRoomList.add(taskRoom);
        });

        //根据考试任务id获取当前任务编排考场数据
        QueryWrapper<TaskArea> taskAreaQueryWrapper = new QueryWrapper<>();
        taskAreaQueryWrapper.lambda()
                .eq(TaskArea::getTaskId, taskId)
                .eq(TaskArea::getDelFlag, false);
        List<TaskArea> currentTaskAreaList = iTaskAreaService.list(taskAreaQueryWrapper);
        if (!CollectionUtils.isEmpty(currentTaskAreaList)) {
            //去掉重复的考试任务编排区域数据，留下新增的考试任务编排区域数据
            List<String> taskAreaIdList = currentTaskAreaList.stream()
                    .map(TaskArea::getAreaId)
                    .collect(Collectors.toList());
            officeAreaList = officeAreaList.stream()
                    .filter(officeArea -> taskAreaIdList.stream()
                            .noneMatch(taskAreaId -> taskAreaId.equals(officeArea.getAreaId())))
                    .collect(Collectors.toList());
        }
        //生成考试任务编排区域数据
        List<TaskArea> taskAreaList = new ArrayList<>();
        officeAreaList.forEach(officeArea -> {
            TaskArea taskArea = new TaskArea();
            BeanUtils.copyProperties(officeArea, taskArea);
            taskArea.setTaskId(taskId).setId(UUIDUtils.newSortUUID());
            taskAreaList.add(taskArea);
        });
        //插入任务考场表
        this.insertTaskRoom(taskRoomList);
        //插入任务考点表
        this.insertTaskPlace(taskPlaceList);
        //插入任务区域表
        this.insertTaskArea(taskAreaList);
    }

    /**
     * 插入任务区域表
     * @param taskAreaList 任务区域数据
     * @throws NestedBusinessException 插入失败，抛出异常
     */
    private void insertTaskArea(List<TaskArea> taskAreaList) throws NestedBusinessException {
        if (!iTaskAreaService.saveBatch(taskAreaList)) {
            throw new NestedBusinessException("插入任务区域表失败");
        }
    }

    /**
     * 插入任务考点表
     * @param taskPlaceList 任务考点数据
     * @throws NestedBusinessException 插入失败，抛出异常
     */
    private void insertTaskPlace(List<TaskPlace> taskPlaceList) throws NestedBusinessException {
        if (!iTaskPlaceService.saveBatch(taskPlaceList)) {
            throw new NestedBusinessException("插入插入任务考点表失败");
        }
    }

    /**
     * 插入任务考场表
     * @param taskRoomList 任务考场数据
     * @throws NestedBusinessException 插入失败，抛出异常
     */
    private void insertTaskRoom(List<TaskRoom> taskRoomList) throws NestedBusinessException {
        if (!saveBatch(taskRoomList)) {
            throw new NestedBusinessException("插入任务考场表失败");
        }
    }

    private List<OfficeArea> getOfficeArea(List<OfficePlace> officePlaceList, String officeId) {
        //根据考点数据获取机构区域编码
        List<String> areaIdList = new ArrayList<>();
        for (OfficePlace place : officePlaceList) {
            areaIdList.add(place.getProvince());
            areaIdList.add(place.getCity());
            areaIdList.add(place.getDistrict());
        }
        areaIdList = areaIdList.stream()
                .distinct()
                .collect(Collectors.toList());
        //根据机构区域编码获取机构区域模板数据
        //return officeAreaMapper.getByAreaIdList(areaIdList, officeId);
        ApiResponse<Object> areaRes = govPtClient.getAreaByIds(areaIdList);
        if(!Objects.equals(20000,areaRes.getCode()) || Objects.isNull(areaRes.getData())){
            throw new GovInvalidRequestException(ResponseCode.ROOM_ADD_PT_AREA_PAGE.getCode(),
                    ResponseCode.ROOM_ADD_PT_AREA_PAGE.getMsg());
        }
        List<AreaDTO> dtos = JSON.parseArray(JSON.toJSONString(areaRes.getData()),AreaDTO.class);
        List<OfficeArea> areas = new ArrayList<>(dtos.size());
        for(AreaDTO dto : dtos){
            OfficeArea area = new OfficeArea();
            area.setAreaId(dto.getId());
            area.setName(dto.getName());
            area.setRemarks(dto.getRemarks());
            area.setSort(dto.getSort());
            area.setType(String.valueOf(dto.getRegionLevel()));
            area.setParentId(String.valueOf(dto.getParentId()));
            area.setParentIds(dto.getParentIds());
            areas.add(area);
        }
        return areas;
    }

    private List<OfficePlace> getOfficePlace(List<OfficeRoom> officeRoomList) {
        //根据考场数据获取考点id
        List<String> placeIdList = officeRoomList.stream()
                .map(OfficeRoom::getPlaceId)
                .distinct()
                .collect(Collectors.toList());
        //根据考点id获取机构考点模板数据
        ApiResponse<Object> taskPlaceRes = govPtClient.getTaskPlace(placeIdList);
        if(!Objects.equals(20000,taskPlaceRes.getCode()) || Objects.isNull(taskPlaceRes.getData())){
            throw new GovInvalidRequestException(ResponseCode.ROOM_ADD_REFRESH_PAGE.getCode(),
                    ResponseCode.ROOM_ADD_REFRESH_PAGE.getMsg());
        }
        List<OfficePlace> list = JSON.parseArray(JSON.toJSONString(taskPlaceRes.getData()),OfficePlace.class);
        return list;
    }

    private List<OfficeRoom> getOfficeRoom(List<String> roomIdList) throws GovInvalidRequestException {
        //校验考场id参数
        long count = roomIdList.stream().distinct().count();
        if (roomIdList.size() != count) {
            //添加的编排考场中存在重复的考场
            throw new GovInvalidRequestException(ResponseCode.ROOM_ADD_ARRANGE_ROOM_EXIST.getCode(),
                    ResponseCode.ROOM_ADD_ARRANGE_ROOM_EXIST.getMsg());
        }
        //根据考场id获取机构考场模板数据
        //List<OfficeRoom> officeRoomList = officeRoomMapper.getByIdList(roomIdList);
        ApiResponse<Object> roomsRes = govPtClient.getRooms(roomIdList);
        if(!Objects.equals(20000,roomsRes.getCode()) || Objects.isNull(roomsRes.getData())){
            throw new GovInvalidRequestException(ResponseCode.ROOM_ADD_PT_ERROR_PAGE.getCode(),
                    ResponseCode.ROOM_ADD_PT_ERROR_PAGE.getMsg());
        }
        List<OfficeRoom> officeRoomList = JSON.parseArray(JSON.toJSONString(roomsRes.getData()),OfficeRoom.class);
        //校验考场数据
        if (roomIdList.size() != officeRoomList.size()) {
            //添加的编排考场中存在已被删除的考场，请刷新页面并重新选择后添加
            throw new GovInvalidRequestException(ResponseCode.ROOM_ADD_REFRESH_PAGE.getCode(),
                    ResponseCode.ROOM_ADD_REFRESH_PAGE.getMsg());
        }
        return officeRoomList;
    }

    /**
     * 查询考场，包含已编排和未编排
     * @param taskTimeRoomArrangeDTO
     * @param taskTimeRoomArrangeDTO
     * @return
     */
    @Override
    public List<TaskTimeRoomArrangeOrNoVO> getRoomArrangeOrNo(TaskTimeRoomArrangeDTO taskTimeRoomArrangeDTO) {
        if(Objects.equals("1",taskTimeRoomArrangeDTO.getQueryType())){
            List<TaskTimeRoomArrangeOrNoVO>  list = baseMapper.selectTimeRoomArrangeOrNoV2(taskTimeRoomArrangeDTO);

            if (list != null && !list.isEmpty()) {
                //处理机构
                List<String> deptIds = list.stream().map(TaskTimeRoomArrangeOrNoVO::getDeptId).filter(Objects::nonNull).collect(Collectors.toList());
                Map<String, String> ancestorsNames = new HashMap<>();
                if(deptIds.size() > 0) {
                    ancestorsNames.putAll(iUaDeptService.getAncestorsNames(deptIds));
                }
                //处理地区
                Map<String,TaskArea> map = iTaskAreaService.getTaskAreaMap(taskTimeRoomArrangeDTO.getTaskId());
                list.stream().forEach(taskTimeRoomArrangeOrNoVO -> {
                    taskTimeRoomArrangeOrNoVO.setDeptName(ancestorsNames.get(taskTimeRoomArrangeOrNoVO.getDeptId()));
                    String province = map.get(taskTimeRoomArrangeOrNoVO.getProvinceId()) == null ? "" : map.get(taskTimeRoomArrangeOrNoVO.getProvinceId()).getName();
                    String city = map.get(taskTimeRoomArrangeOrNoVO.getCityId()) == null ? "" : map.get(taskTimeRoomArrangeOrNoVO.getCityId()).getName();
                    String discrit = map.get(taskTimeRoomArrangeOrNoVO.getDistrictId()) == null ? "" : map.get(taskTimeRoomArrangeOrNoVO.getDistrictId()).getName();
                    StringBuilder sb = new StringBuilder();
                    if(StringUtils.isNotBlank(province)){
                        sb.append(province).append("/");
                    }
                    if(StringUtils.isNotBlank(city)){
                        sb.append(city).append("/");
                    }
                    if(StringUtils.isNotBlank(discrit)){
                        sb.append(discrit).append("/");
                    }
                    String areaName = sb.toString();
                    if (StringUtils.isNotBlank(areaName) && areaName.length() == (areaName.lastIndexOf("/") + 1)) {
                        areaName = areaName.substring(0, areaName.lastIndexOf("/"));
                    }
                    taskTimeRoomArrangeOrNoVO.setProvinceName(province);
                    taskTimeRoomArrangeOrNoVO.setCityName(city);
                    taskTimeRoomArrangeOrNoVO.setDistrictName(discrit);
                    taskTimeRoomArrangeOrNoVO.setAreaName(areaName);
                });
            }
            return list;
        }
        //查询未选择考场
        List<TaskTimeRoomArrangeOrNoVO> list = baseMapper.selectTimeRoomArrangeNoSelected(taskTimeRoomArrangeDTO);
        if(!CollectionUtils.isEmpty(list)){
            //去掉已经被选择了的
            List<String> roomIds = baseMapper.selectTimeRoomIdSelected(taskTimeRoomArrangeDTO.getTaskId(), taskTimeRoomArrangeDTO.getTimeId());
            Iterator<TaskTimeRoomArrangeOrNoVO> iterator = list.listIterator();
            while (iterator.hasNext()){
                TaskTimeRoomArrangeOrNoVO vo = iterator.next();
                if(Objects.nonNull(vo.getRoomId()) && roomIds.contains(vo.getRoomId())){
                    iterator.remove();
                }
            }
            //处理机构
            List<String> deptIds = list.stream().map(TaskTimeRoomArrangeOrNoVO::getDeptId).filter(Objects::nonNull).collect(Collectors.toList());
            Map<String, String> ancestorsNames = new HashMap<>();
            if(deptIds.size() > 0) {
                ancestorsNames.putAll(iUaDeptService.getAncestorsNames(deptIds));
            }
            //处理地区
            Map<String,TaskArea> map = iTaskAreaService.getTaskAreaMap(taskTimeRoomArrangeDTO.getTaskId());
            list.stream().forEach(taskTimeRoomArrangeOrNoVO -> {
                taskTimeRoomArrangeOrNoVO.setProvinceName(Optional.ofNullable(map.get(taskTimeRoomArrangeOrNoVO.getProvinceId())).map(TaskArea::getName).get())
                        .setCityName(Optional.ofNullable(map.get(taskTimeRoomArrangeOrNoVO.getCityId())).map(TaskArea::getName).get())
                        .setDistrictName(Optional.ofNullable(map.get(taskTimeRoomArrangeOrNoVO.getDistrictId())).map(TaskArea::getName).get());
                taskTimeRoomArrangeOrNoVO.setDeptName(ancestorsNames.get(taskTimeRoomArrangeOrNoVO.getDeptId()));
            });
            return list;
        }
        return list;
    }

    @Override
    public Page<TaskRoomQueryResultVO> getTaskRoom(Page<TaskRoomQueryResultVO> pageParam, String taskId) {
        String level = iArrangeService.JudgeAreaLevel(taskId);
        Page<TaskRoomQueryResultVO> taskRoomQueryResultVoPage = baseMapper.selectTaskRoomByTaskId(pageParam, taskId);
        List<TaskRoomQueryResultVO> records = taskRoomQueryResultVoPage.getRecords();
        if (records.isEmpty()) {
            return taskRoomQueryResultVoPage;
        }
        Map<String, TaskArea> taskAreaMap = iTaskAreaService.lambdaQuery().eq(TaskArea::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).list().stream().collect(Collectors.toMap(TaskArea::getAreaId, Function.identity()));
        records.forEach(item -> {
            String provinceId = item.getProvinceName();
            if(StringUtils.isNotBlank(provinceId)) {
                TaskArea province = taskAreaMap.get(provinceId);
                StringBuffer sb = new StringBuffer();
                if(province != null) {
                    item.setProvinceName(province.getName());
                    sb.append("/" + province.getName());
                } else {
                    item.setProvinceName("");
                }
                TaskArea city = taskAreaMap.get(item.getCityName());
                if(city != null) {
                    item.setCityName(city.getName());
                    sb.append("/" + city.getName());
                } else {
                    item.setCityName("");
                }
                TaskArea district = taskAreaMap.get(item.getDistrictName());
                if(district != null) {
                    item.setDistrictName(district.getName());
                    sb.append("/" + district.getName());
                } else {
                    item.setDistrictName("");
                }
                if(sb.length() > 0) {
                    item.setAreaName(sb.substring(1));
                    if (StringUtils.isNotBlank(item.getAreaName())) {
                        String areaFullName = item.getAreaName();
                        if(String.valueOf(SelfArrangeEnum.ARRANGE_BY_LEVEL_ONE.getCode()).equals(level)){
                            areaFullName = areaFullName.substring(0,areaFullName.indexOf("/"));
                        }else if(String.valueOf(SelfArrangeEnum.ARRANGE_BY_LEVEL_TWO.getCode()).equals(level)){
                            areaFullName = areaFullName.substring(0,areaFullName.lastIndexOf("/"));
                        }
                        item.setAreaName(areaFullName);
                    }
                }
            }
            if(StringUtils.isNotBlank(item.getDept())) {
                Map<String, String> ancestorsNames = iUaDeptService.getAncestorsNames(Arrays.asList(item.getDept()));
                item.setDept(ancestorsNames.get(item.getDept()));
            }
        });
        return taskRoomQueryResultVoPage;
    }

    @Override
    public TaskRoomTotalVO getTaskRoomTotal(String taskId) {
        return baseMapper.selectTaskRoomTotalByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTaskRoom(String taskId, String roomIds) {
        this.deleteCheck(taskId);
        if(StringUtils.isNotBlank(roomIds)) {
            super.removeByIds(Arrays.asList(roomIds.split(",")));
            //删除考点数据
            this.removeTaskPlace(taskId);
        }
    }

    /**
     * 删除考点数据
     * @param taskId 考试任务id
     */
    private void removeTaskPlace(String taskId) {
        //获取考场表中的所有考点id
        List<String> roomPlaceIdList = this.lambdaQuery()
                .select(TaskRoom::getPlaceId)
                .eq(TaskRoom::getTaskId, taskId)
                .eq(TaskRoom::getDelFlag, Boolean.FALSE)
                .list()
                .stream()
                .map(TaskRoom::getPlaceId)
                .distinct()
                .collect(Collectors.toList());
        //获取考点表中所有的考点id
        List<String> placeIdList = iTaskPlaceService.lambdaQuery()
                .select(TaskPlace::getId)
                .eq(TaskPlace::getTaskId, taskId)
                .eq(TaskPlace::getDelFlag, Boolean.FALSE)
                .list()
                .stream()
                .map(TaskPlace::getId)
                .collect(Collectors.toList());
        //找出没有考场的考点，即placeIdList - roomPlaceIdList的差集，即placeIdList中有的id 但 roomPlaceIdList中没有的
        List<String> withoutRoomPlaceIdList = placeIdList.stream().filter(placeId -> !roomPlaceIdList.contains(placeId))
                .collect(Collectors.toList());
        //删除没有考场的考点
        iTaskPlaceService.removeByIds(withoutRoomPlaceIdList);
    }

    @Override
    public List<TaskRoom> getByCode(String code) {
        QueryWrapper<TaskRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(TaskRoom::getCode, code)
                .eq(TaskRoom::getDelFlag, false);
        return list(queryWrapper);
    }

    @Override
    public TaskRoom getByTaskCodeAndCode(String taskCode, String code) {
        return taskRoomMapper.getByTaskCodeAndCode(taskCode, code);
    }

    @Override
    public Boolean existByTaskCodeAndCode(String taskCode, String code) {
        Boolean exist = baseMapper.existByTaskCodeAndCode(taskCode, code);
        return Optional.ofNullable(exist).orElse(false);
    }

    @Override
    public List<TaskRoom> getTaskRoom(String taskId, String placeId) {
        return lambdaQuery()
                .eq(TaskRoom::getTaskId,taskId)
                .eq(TaskRoom::getPlaceId,placeId)
                .eq(TaskRoom::getDelFlag,false)
                .orderByAsc(TaskRoom::getCode)
                .list();
    }

    @Override
    public RegisterInfoGceResult getGceRegisterRoomInfo(String taskCode, String code) {
        return taskRoomMapper.getGceRegisterRoomInfo(taskCode, code);
    }

    @Override
    public RegisterInfoObtResult getObtRegisterRoomInfo(String taskCode, String code) {
        return taskRoomMapper.getObtRegisterRoomInfo(taskCode, code);
    }

    @Override
    public List<RoomInfoResult> getRoomInfoResult(String taskCode, String placeCode) {
        return taskRoomMapper.getRoomInfoResult(taskCode, placeCode);
    }

    @Override
    public Integer getSeatNumberByCode(String taskCode, String roomCode) {
        return taskRoomMapper.getSeatNumberByCode(taskCode, roomCode);
    }

    @Override
    public List<TaskRoom> listByTaskId(String taskId) {
        return lambdaQuery()
                .eq(TaskRoom::getTaskId,taskId)
                .eq(TaskRoom::getDelFlag,false)
                .list();
    }

    @Override
    public Map<String,TaskRoom> mapByTaskId(String taskId) {
        List<TaskRoom> taskRoomList = listByTaskId(taskId);
        Map<String,TaskRoom> map = new HashMap<>();
        taskRoomList.stream().forEach(taskRoom -> {
            map.put(taskRoom.getId(),taskRoom);
        });

        return map;
    }

    private List<OfficeAreaSheet> getOfficeAreaSheet(){
        List<OfficeAreaSheet> officeAreaSheets = new ArrayList<>();
        // 二级
        List<TaskArea> districtList = new ArrayList<>();
        // 三级
        Map<String, TaskArea> dictionaryMap = new HashMap<>(3);
        List<TaskArea> allAreas = iOfficeAreaService.getAllAreaList();
        // 得到字典,和县级的数据
        for (TaskArea taskArea : allAreas){
            if(3 == taskArea.getRegionLevel()) {
                districtList.add(taskArea);
            }
            dictionaryMap.put(taskArea.getAreaId(), taskArea);
        }
        // 组装数据
        for(TaskArea taskArea : districtList){
            OfficeAreaSheet officeAreaSheet = new OfficeAreaSheet();
            officeAreaSheet.setDistrictCode(taskArea.getAreaId());
            officeAreaSheet.setDistrictName(taskArea.getName());
            TaskArea cityData = dictionaryMap.get(taskArea.getParentId());
            if(null != cityData){
                officeAreaSheet.setCityCode(cityData.getAreaId());
                officeAreaSheet.setCityName(cityData.getName());
                TaskArea provinceData = dictionaryMap.get(cityData.getParentId());
                if(null != provinceData) {
                    officeAreaSheet.setProvinceCode(provinceData.getAreaId());
                    officeAreaSheet.setProvinceName(provinceData.getName());
                }
            }
            officeAreaSheets.add(officeAreaSheet);
        }
        // 返回数据
        return officeAreaSheets;
    }

    @Override
    public void downloadTemplate(HttpServletResponse response, User user) throws IOException {
        ExcelWriter writer = null;
        try {
            String fileName = URLEncoder.encode("导入考点考场信息模板", "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            ClassPathResource resource = new ClassPathResource("templates/room/room.xlsx");
            ServletOutputStream out = response.getOutputStream();
            writer = EasyExcel.write(out).withTemplate(resource.getInputStream()).build();
            WriteSheet sheet1 = EasyExcel.writerSheet(0).build();
            WriteSheet sheet2 = EasyExcel.writerSheet(1).build();
            // 所属组织
            writer.write(getDeptData(user), sheet1);
            // 区域
            writer.write(getOfficeAreaSheet(), sheet2);
        } finally {
            if(writer != null) {
                writer.finish();
            }
        }
    }

    /**
     * 当前用户所在机构的所有组织层级，不限制当前用户的层级
     * @return
     */
    @Override
    public List<List<String>> getDeptData(User user){
        List<List<String>> officeAreaSheets = new ArrayList<>();
        Set<String> deptSets = new TreeSet<>();
        UserOrgAccessDTO userAccessDeptIds = iUaDeptAccessService.getUserAccessDeptIds(user.getId());
        List<Tree> officeTree = new ArrayList<>();
        if(!userAccessDeptIds.getAllPermission()) { //不是所有数据权限
            // 获取当前用户所拥有的数据权限
            deptSets.addAll(userAccessDeptIds.getAccessOrgIds());
            QueryOrgsParam queryOrgsParam = new QueryOrgsParam();
            queryOrgsParam.setOrgids(new ArrayList<>(deptSets));
            ApiResponse<List<DeptVO>> listApiResponse = iUaDeptService.queryDeptsByIds(queryOrgsParam);
            if(Objects.isNull(listApiResponse) || Objects.isNull(listApiResponse.getData())){
                return officeAreaSheets;
            }
            List<DeptVO> vos = listApiResponse.getData();
            officeTree = vos.stream().map(dept -> {
                Tree tree = new Tree();
                tree.setId(dept.getId())
                        .setPid(dept.getPid())
                        .setText(dept.getName())
                        .setObj(dept);
                return tree;
            }).collect(Collectors.toList());
        } else {
            officeTree = iUaDeptService.getOfficeTree();
        }

        if(officeTree != null && officeTree.size() > 0) {
            List<List<Tree>> lists = TreeToListConverter.convertTreeToList(officeTree);
            for (int i = 0; i < lists.size(); i++) {
                List<Tree> trees = lists.get(i);
                List<String> data = new ArrayList<>(trees.size());
                for (int i1 = 0; i1 < trees.size(); i1++) {
                    Tree tree = trees.get(i1);
                    data.add(tree.getText());
                    data.add(tree.getId());
                }
                if(data.size() > 0) {
                    officeAreaSheets.add(data);
                }
            }
        }
        // 返回数据
        return officeAreaSheets;
    }

    @SneakyThrows
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importExcel(String fileUrl, String taskId) {
        this.check(taskId);

        String fileSuffix = FileUtil.getSuffix(fileUrl);
        if (!StrUtil.equalsAny(fileSuffix, "xlsx", "xls")) {
            BusinessLogicException.throwException(43330403, "上传的文件类型不匹配");
        }
        File uploadFile = FileUtil.createTempFile(StrPool.DOT + fileSuffix, Boolean.TRUE);
        long fileSize = HttpUtil.downloadFile(fileUrl, uploadFile);
        if (fileSize == CommonConstant.ZERO) {
            BusinessLogicException.throwException(43330402, "请勿上传一个空文件");
        }
        ProgressManager.updateProgressCurrent(3L, "文件接收成功");
        // 异常信息
        List<ExcelErr> errList = new ArrayList<>();
        //校验excel文件
        this.excelVerify(uploadFile);
        ProgressManager.updateProgressCurrent(5L, "Excel文件格式校验完成");

        List<ImportPlaceRoomSheet> placeRoomList = new ArrayList<>();
        List<ImportSelfAreaSheet> areaList =  new ArrayList<>();

        // 读取excel
        this.readExcel(uploadFile, placeRoomList, areaList);
        ProgressManager.updateProgressCurrent(20L, "Excel文件读取完成");
        // 备份文件

        List<TaskPlace> placeList = new ArrayList<>();
        List<TaskRoom> roomList = new ArrayList<>();
        Map<String, ImportSelfAreaSheet> districtMap = new HashMap<>();

        // 一个sheet异常记录
        ExcelErr sheet3Err = ExcelErr.builder().sheetNo(1).sheetName("区域信息").build();
        List<TaskArea> selfLevelList = new ArrayList<>();
        List<ExcelRowErr> row3Errs = this.analysisSelfLevelExcelData(taskId, areaList, selfLevelList, districtMap);
        if(Objects.nonNull(row3Errs) && row3Errs.size() > 0) {
            sheet3Err.setRowErrList(row3Errs);
            errList.add(sheet3Err);
        }
        ProgressManager.updateProgressCurrent(50L, "Excel文件区域信息校验完成");
        // 一个sheet异常记录
        ExcelErr sheet2Err = ExcelErr.builder().sheetNo(2).sheetName("考点考场信息").build();
        // 解析考点考场sheet
        List<ExcelRowErr> rowErrs = this.analysisExcelData(taskId, placeRoomList, placeList, roomList, districtMap);
        if(Objects.nonNull(rowErrs) && rowErrs.size() > 0) {
            sheet2Err.setRowErrList(rowErrs);
            errList.add(sheet2Err);
        }

        ProgressManager.updateProgressCurrent(77L, "Excel文件考点考场信息校验完成");

        if (CollUtil.isNotEmpty(errList)) {
            ProgressManager.updateProgressCurrent(99L, "Excel文件导入完成");
            ProgressManager.finish(ProgressResult.builder().success(Boolean.FALSE).result(errList).build());
            return;
        }

        ProgressManager.updateProgressCurrent(81L, "数据录入中");

        // 插入数据
        if (!CollectionUtils.isEmpty(placeList)) {
            iTaskPlaceService.saveBatch(placeList);
        }
        if (!CollectionUtils.isEmpty(roomList)) {
            this.saveBatch(roomList);
        }
        if (!CollectionUtils.isEmpty(selfLevelList)) {
            iTaskAreaService.saveOrUpdateBatch(selfLevelList);
        }
        ProgressManager.updateProgressCurrent(99L, "数据录入中");
        ProgressManager.finish(ProgressResult.builder().success(Boolean.TRUE).build());
    }

    private void readExcel(File file, List<ImportPlaceRoomSheet> placeRoomList, List<ImportSelfAreaSheet> areaList) {
        ExcelReader excelReader = EasyExcel.read(file).build();
        PlaceRoomListener placeRoomListener = new PlaceRoomListener();
        SelfLevelListener selfLevelListener = new SelfLevelListener();
        // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
        ReadSheet areSheet = EasyExcel.readSheet(1).head(ImportSelfAreaSheet.class).registerReadListener(selfLevelListener).build();
        ReadSheet placeRoomSheet = EasyExcel.readSheet(2).head(ImportPlaceRoomSheet.class).registerReadListener(placeRoomListener).build();
        // 这里注意 一定要把sheet1 sheet2 一起传进去，不然有个问题就是03版的excel 会读取多次，浪费性能
        excelReader.read(areSheet, placeRoomSheet);
        placeRoomList.addAll(placeRoomListener.getImportTaskCandidates());
        areaList.addAll(selfLevelListener.getDataList());
    }

    /**
     * 构造自定义层级数据
     * @param areaSheetList
     */
    public List<ExcelRowErr> analysisSelfLevelExcelData(String taskId, List<ImportSelfAreaSheet> areaSheetList,
                                                        List<TaskArea> selfLevelList, Map<String, ImportSelfAreaSheet> districtMap){
        List<ExcelRowErr> row3Errs = new ArrayList<>();
        if (CollectionUtils.isEmpty(areaSheetList)) {
            return row3Errs;
        }

        List<ImportSelfAreaSheet> dataList = new ArrayList<>();

        for (int i = 0; i < areaSheetList.size(); i++) {
            List<ExcelLineErr> lineErrs = new ArrayList<>();

            ImportSelfAreaSheet area = areaSheetList.get(i);
            if (StringUtils.isEmpty(area.getProvinceName())) {
                lineErrs.add(ExcelLineErr.builder().line(0).msg("省名称不能为空").build());
            }
            if (StringUtils.isEmpty(area.getProvinceCode())) {
                lineErrs.add(ExcelLineErr.builder().line(1).msg("省编码不能为空").build());
            }
            if (StringUtils.isEmpty(area.getCityName())) {
                lineErrs.add(ExcelLineErr.builder().line(2).msg("市名称不能为空").build());
            }
            if ( StringUtils.isEmpty(area.getCityCode())) {
                lineErrs.add(ExcelLineErr.builder().line(3).msg("市编码不能为空").build());
            }
            if (StringUtils.isEmpty(area.getDistrictName())) {
                lineErrs.add(ExcelLineErr.builder().line(4).msg("区县名称不能为空").build());
            }
            if (StringUtils.isEmpty(area.getDistrictCode())) {
                lineErrs.add(ExcelLineErr.builder().line(5).msg("区县编码不能为空").build());
            }
            if(lineErrs.size() > 0) {
                row3Errs.add(ExcelRowErr.builder().row(i).lineErrList(lineErrs).build());
                continue;
            }
            dataList.add(area);
            districtMap.put(area.getDistrictCode(), area);
        }

        if(dataList.size() == 0) {
            return row3Errs;
        }

        List<TaskArea> db_data = iTaskAreaService.lambdaQuery().eq(TaskArea::getTaskId, taskId).list();
        Map<String, String> db_map = new HashMap<>();
        if(db_data.size() > 0) {
            db_map.putAll(db_data.stream().collect(Collectors.toMap(TaskArea::getAreaId, TaskArea::getName)));
        }

        String countryCode = "0";
        //筛选省数据
        List<ImportSelfAreaSheet> provinceList = dataList.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ImportSelfAreaSheet::getProvinceCode))), ArrayList::new
                ));
        //筛选市数据
        List<ImportSelfAreaSheet> cityList = dataList.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ImportSelfAreaSheet::getCityCode))), ArrayList::new
                ));
        //筛选区县数据
        List<ImportSelfAreaSheet> districtList = dataList.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ImportSelfAreaSheet::getDistrictCode))), ArrayList::new
                ));

        //组装省
        List<TaskArea> provinceAreaList = provinceList.stream().filter(item -> !db_map.containsKey(item.getProvinceCode())).map(province -> {
            TaskArea officeArea = new TaskArea();
            officeArea.setAreaId(province.getProvinceCode())
                    .setTaskId(taskId)
                    .setParentId("0")
                    .setParentIds("0")
                    .setName(province.getProvinceName())
                    .setType("2")
                    .setId(UUIDUtils.randomUUID());
            return officeArea;
        }).collect(Collectors.toList());
        selfLevelList.addAll(provinceAreaList);
        //组装市
        List<TaskArea> cityAreaList = cityList.stream().filter(item -> !db_map.containsKey(item.getCityCode())).map(city -> {
            TaskArea officeArea = new TaskArea();
            officeArea.setAreaId(city.getCityCode())
                    .setTaskId(taskId)
                    .setParentId(city.getProvinceCode())
                    .setParentIds(countryCode +  "," + city.getProvinceCode())
                    .setName(city.getCityName())
                    .setType("3")
                    .setId(UUIDUtils.randomUUID());
            return officeArea;
        }).collect(Collectors.toList());
        selfLevelList.addAll(cityAreaList);
        //组装区县
        List<TaskArea> districtAreaList = districtList.stream().filter(item -> !db_map.containsKey(item.getDistrictCode())).map(district -> {
            TaskArea officeArea = new TaskArea();
            officeArea.setAreaId(district.getDistrictCode())
                    .setTaskId(taskId)
                    .setParentId(district.getCityCode())
                    .setParentIds(countryCode + "," + district.getProvinceCode() + "," + district.getCityCode())
                    .setName(district.getDistrictName())
                    .setType("4")
                    .setId(UUIDUtils.randomUUID());
            return officeArea;
        }).collect(Collectors.toList());
        selfLevelList.addAll(districtAreaList);
        return row3Errs;
    }

    private int getMaxSort(String taskId, int type) {
        if(type == 1) {
            TaskPlace taskPlace = iTaskPlaceService.lambdaQuery().eq(TaskPlace::getTaskId, taskId).eq(TaskPlace::getDelFlag, false).orderByDesc(TaskPlace::getCode).last("limit 1").one();
            return Objects.isNull(taskPlace) || Objects.isNull(taskPlace.getCode()) ? 0 : taskPlace.getCode();
        } else {
            TaskRoom taskRoom = this.lambdaQuery().eq(TaskRoom::getTaskId, taskId).eq(TaskRoom::getDelFlag, false).orderByDesc(TaskRoom::getCode).last("limit 1").one();
            return Objects.isNull(taskRoom) || Objects.isNull(taskRoom.getCode()) ? 0 : taskRoom.getCode();
        }
    }

    private Map<String, String> getAllDeptMap() {
        Map<String, String> map = new HashMap<>();
        List<Organization> officeAllDept = iUaDeptService.getOfficeAllDepts();
        if(officeAllDept != null && officeAllDept.size() > 0) {
            for (Organization organization : officeAllDept) {
                map.put(organization.getId(), organization.getCode());
            }
        }
        return map;
    }

    protected List<ExcelRowErr> analysisExcelData(String taskId, List<ImportPlaceRoomSheet> excelData, List<TaskPlace> officePlaceList,
                                     List<TaskRoom> officeRoomList, Map<String, ImportSelfAreaSheet> selfLevelMap) throws KqException {
        List<ExcelRowErr> errList = new ArrayList<>();
        Map<String, String> placeNameExcelRepeat = new HashMap<>();

        Integer mapPlaceMaxSort = this.getMaxSort(taskId, 1);
        Integer mapRoomMaxSort = this.getMaxSort(taskId, 2);

        Map<String, String> deptMap = this.getAllDeptMap();

        // 检验数据
        for (int i = 0; i < excelData.size(); i++) {
            ImportPlaceRoomSheet placeRoomData = excelData.get(i);
            // 校验数据
            List<ExcelLineErr> rowErrs = this.checkValue(placeRoomData, deptMap, selfLevelMap);
            if(rowErrs.size() > 0) {
                errList.add(ExcelRowErr.builder().row(i).lineErrList(rowErrs).build());
                continue;
            }
            ImportSelfAreaSheet s = selfLevelMap.get(placeRoomData.getAreaCode());
            // 考点数据
            TaskPlace officePlace = new TaskPlace();
            officePlace.setName(placeRoomData.getPlaceName())
                    .setTaskId(taskId)
                    .setAddress(placeRoomData.getPlaceAddress())
                    .setTrafficGuide(placeRoomData.getTrafficGuide())
                    .setLinkman(placeRoomData.getLinkman())
                    .setPhone(placeRoomData.getPhone())
                    .setProvince(s.getProvinceCode())
                    .setCity(s.getCityCode())
                    .setDistrict(s.getDistrictCode())
                    .setDeptId(placeRoomData.getDept());
            // 考场数据
            TaskRoom officeRoom = new TaskRoom();
            officeRoom.setName(placeRoomData.getRoomName())
                    .setTaskId(taskId)
                    .setAddress(placeRoomData.getRoomAddress())
                    .setSeatNum(Integer.parseInt(placeRoomData.getSeatNum()))
                    .setReserveSeatNum(Integer.parseInt(placeRoomData.getReserveSeatNum()))
                    .setAnswerType(Integer.parseInt(placeRoomData.getAnswerType()))
                    .setReserve("1".equals(placeRoomData.getIsReserve()));

            String placeId = UUIDUtils.newSortUUID();
            // 校验考点，重名则不新增
            TaskPlace officePlaceOld = iTaskPlaceService.lambdaQuery().eq(TaskPlace::getTaskId, taskId)
                    .eq(TaskPlace::getName, officePlace.getName()).eq(BaseEntity::getDelFlag, false).last("limit 1").one();
            if (null == officePlaceOld) {
                officePlace.setId(placeId);
                officePlace.setCode(++mapPlaceMaxSort);
                // 插入机构考点模板表
                officePlaceOld = officePlace;
                // 防止重复添加考点
                if(null == placeNameExcelRepeat.get(officePlace.getName())){
                    placeNameExcelRepeat.put(officePlace.getName(), placeId);
                    officePlaceList.add(officePlace);
                }else{
                    // 防止插入不同id
                    placeId = placeNameExcelRepeat.get(officePlace.getName());
                }
            }else{
                // 如果存在，就用查出来的placeId
                placeId = officePlaceOld.getId();
            }
            officeRoom.setId(UUIDUtils.newSortUUID());
            officeRoom.setPlaceId(placeId);
            // 校验考场参数
            //checkRoomByAdd(officeRoom);
            // 校验通过放入列表
            officeRoom.setCode(++mapRoomMaxSort);
            officeRoomList.add(officeRoom);
        }
        return errList;
    }

    /**
     * 校验参数
     * @param placeRoomData
     */
    private List<ExcelLineErr> checkValue(ImportPlaceRoomSheet placeRoomData, Map<String, String> deptMap, Map<String, ImportSelfAreaSheet> areaMap) {
        List<ExcelLineErr> rowErrs = new ArrayList<>();
        if(StringUtils.isNotEmpty(placeRoomData.getDept()) && !deptMap.containsKey(placeRoomData.getDept())){
            rowErrs.add(ExcelLineErr.builder().line(0).msg("所属组织不正确").build());
        }
        if(StringUtils.isNotEmpty(placeRoomData.getAreaCode()) && !areaMap.containsKey(placeRoomData.getAreaCode())) {
            rowErrs.add(ExcelLineErr.builder().line(1).msg("区域编码不正确").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getPlaceName())){
            rowErrs.add(ExcelLineErr.builder().line(2).msg("考点名称不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getPlaceAddress())){
            rowErrs.add(ExcelLineErr.builder().line(3).msg("考点地址不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getTrafficGuide())){
            rowErrs.add(ExcelLineErr.builder().line(4).msg("交通指南不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getLinkman())){
            rowErrs.add(ExcelLineErr.builder().line(5).msg("联系人不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getPhone()) || !TextUtil.isPhoneNum(placeRoomData.getPhone())){
            rowErrs.add(ExcelLineErr.builder().line(6).msg("联系电话不能正确").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getRoomName())){
            rowErrs.add(ExcelLineErr.builder().line(7).msg("考场名称不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getRoomAddress())){
            rowErrs.add(ExcelLineErr.builder().line(8).msg("考场地址不能为空").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getSeatNum()) || !TextUtil.isInteger(placeRoomData.getSeatNum())){
            rowErrs.add(ExcelLineErr.builder().line(9).msg("机位数不能为空且必须是正整数").build());
        }
        if(StringUtils.isEmpty(placeRoomData.getReserveSeatNum())){
            rowErrs.add(ExcelLineErr.builder().line(10).msg("备用机位数不能为空且必须是正整数").build());
        }

        if(StringUtils.isEmpty(placeRoomData.getAnswerType()) || !placeRoomData.getAnswerType().matches(regex[1])){
            rowErrs.add(ExcelLineErr.builder().line(11).msg("作答方式不能为空且必须只能是数字1或2").build());
        }
        return rowErrs;
    }

    @Override
    public void updateClosureByCode(String taskCode, String roomCode) {
        taskRoomMapper.updateClosureByCode(taskCode, roomCode);
    }

    protected void excelVerify(File file) throws KqException {
        String fileName = file.getName();

        if (fileName == null) {
            //导入考场信息文件类型错误
            throw new KqException(ResponseCode.PLACE_ROOM_IMPORT_FILE_TYPE_ERROR.getCode(),
                    ResponseCode.PLACE_ROOM_IMPORT_FILE_TYPE_ERROR.getMsg());
        }
        String ext = FilenameUtils.getExtension(fileName);
        if (!ext.equalsIgnoreCase(StringCodeEnum.EXCEL_XLS.getCode())
                && !ext.equalsIgnoreCase(StringCodeEnum.EXCEL_XLSX.getCode())) {
            //导入考场信息文件类型错误
            throw new KqException(ResponseCode.PLACE_ROOM_IMPORT_FILE_TYPE_ERROR.getCode(),
                    ResponseCode.PLACE_ROOM_IMPORT_FILE_TYPE_ERROR.getMsg());
        }
    }

    /**
     * 读取excel数据
     *
     * @param file excel文件
     * @return 读取完毕后的excel内容
     * @throws IOException IO异常
     */
    protected List<ImportPlaceRoomSheet> readExcel(MultipartFile file) throws IOException {
        //读取excel
        PlaceRoomListener placeRoomListener = new PlaceRoomListener();
        EasyExcel.read(file.getInputStream()).head(ImportPlaceRoomSheet.class).
                registerReadListener(placeRoomListener). sheet(2). doReadSync();
        return placeRoomListener.getImportTaskCandidates();
    }

    /**
     * 校验添加考点参数
     * @param place 添加考点参数
     * @param officeId 机构id
     * @throws GovInvalidRequestException 校验不通过，抛出异常
     */
    private OfficePlace placeNameExist(OfficePlace place, String officeId) throws GovInvalidRequestException {
        //获取当前机构下所有考点
        QueryWrapper<OfficePlace> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OfficePlace::getOfficeId, officeId)
                .eq(OfficePlace::getDelFlag, false);
        List<OfficePlace> officePlaceList = iOfficePlaceService.list(queryWrapper);
        //校验考点名称是否重复
        if (!CollectionUtils.isEmpty(officePlaceList)) {
            String name = place.getName();
            List<OfficePlace> officePlaces = officePlaceList.stream().filter(item -> item.getName().equals(name)).collect(Collectors.toList());
            if (officePlaces.size() > 0) {
                return officePlaces.get(0);
            }
        }
        return null;
    }


    /**
     * 校验添加考场参数
     * @param officeRoom 考场参数
     * @throws GovInvalidRequestException 考场参数校验不通过，抛出异常
     */
    private void checkRoomByAdd(OfficeRoom officeRoom) throws KqException {
        //获取当前考点下所有考场
        QueryWrapper<OfficeRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(OfficeRoom::getPlaceId, officeRoom.getPlaceId())
                .eq(OfficeRoom::getDelFlag, false);
        List<OfficeRoom> officeRoomList = iOfficeRoomService.list(queryWrapper);
        //校验考场名称是否重复
        /*if (!CollectionUtils.isEmpty(officeRoomList)) {
            String name = officeRoom.getName();
            boolean repeat = officeRoomList.stream()
                    .anyMatch(item -> item.getName().equals(name));
            if (repeat) {
                //当前考点下存在名称相同的考场
                throw new KwGovException(ResponseCode.PLACE_ROOM_FILE_IMPORT_ERROR, "当前考点下存在名称相同的考场");
            }
        }*/
    }

    @Override
    public void deleteAllByTaskId(String taskId) {
        QueryWrapper<TaskRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(TaskRoom::getTaskId, taskId);
        remove(queryWrapper);
    }

    @Override
    public List<TaskRoom> getTaskRoomAndCreate(String taskId, Integer roomCounts, Integer roomMaxCandidateCount) {

        //获取考点信息
        TaskPlace taskPlace = iTaskPlaceService.getOneTaskPlace(taskId);

        //如果现有的考场数够，则无需再创建
        List<TaskRoom> taskRoomList = getTaskRoom(taskId,taskPlace.getId());
        if (taskRoomList != null && taskRoomList.size() >=  roomCounts) {
            return taskRoomList;
        }

        //考场数不够，创建考场
        Integer maxCode = 0;
        if (taskRoomList != null && !taskRoomList.isEmpty()) {
            maxCode = taskRoomList.stream().max(Comparator.comparing(TaskRoom::getCode)).get().getCode();
        }

        List<TaskRoom> taskRooms = new ArrayList<>();
        for (int i = maxCode; i < roomCounts; i++) {
            TaskRoom taskRoom = new TaskRoom();
            int code = i + 1;
            taskRoom.setTaskId(taskId)
                    .setPlaceId(taskPlace.getId())
                    .setCode(code)
                    .setName("虚拟考场" + code)
                    .setSeatNum(roomMaxCandidateCount)
                    .setReserveSeatNum(0)
                    .setAnswerType(1)
                    .setReserve(false)
                    .setRegistered(false)
                    .setClosure(false)
                    .setId(UUIDUtils.newSortUUID());
            taskRooms.add(taskRoom);
        }

        //虚拟考场入库
        this.saveBatch(taskRooms);

        //合并考场
        taskRoomList = (taskRoomList == null) ? new LinkedList<>() : taskRoomList;
        taskRoomList.addAll(taskRooms);

        //排序
        return taskRoomList.stream()
                .sorted(Comparator.comparing(TaskRoom::getCode))
                .collect(Collectors.toList());
    }

    @Override
    public boolean hasRoom(String taskId, String roomId) {
        return this.lambdaQuery().select(TaskRoom::getId)
                .eq(TaskRoom::getTaskId, taskId)
                .eq(TaskRoom::getId, roomId)
                .eq(TaskRoom::getDelFlag, false)
                .last("LIMIT 1")
                .oneOpt()
                .isPresent();
    }

    @Override
    public Map<String, TaskRoom> getRoomMap(String taskId) {
        Map<String, TaskRoom> map = new HashMap<>();
        List<TaskRoom> list = this.lambdaQuery().eq(TaskRoom::getTaskId, taskId).eq(TaskRoom::getDelFlag, false).list();
        for (TaskRoom taskRoom : list) {
            map.put(String.valueOf(taskRoom.getCode()), taskRoom);
        }
        return map;
    }

    @Override
    public List<ArrangeRoomVO> getAreaArrangeRoomList(String taskId, String placeProvince) {
        return baseMapper.getAreaArrangeRoomList(taskId, placeProvince);
    }

    @SneakyThrows
    @Override
    public void exportExcel(String taskId) {
        // 文件名称
        String lastFileName = UUIDUtils.randomUUID() + ".xlsx";
        File tempFile = FileUtil.createTempFile(lastFileName, Boolean.TRUE);
        tempFile.deleteOnExit();
        try {
            Map<String, String> deptMap = new HashMap<>();
            ProgressManager.updateProgressCurrent(10L, "数据准备中");
            List<ExportPlaceRoomDTO> dataList = baseMapper.getExportRoomList(taskId);
            Map<String, TaskArea> taskAreaMap = iTaskAreaService.lambdaQuery().eq(TaskArea::getTaskId, taskId).eq(BaseEntity::getDelFlag, false)
                    .list().stream().collect(Collectors.toMap(TaskArea::getAreaId, Function.identity()));
            dataList.forEach(item -> {
                String provinceId = item.getProvince();
                if(StringUtils.isNotBlank(provinceId)) {
                    TaskArea province = taskAreaMap.get(provinceId);
                    StringBuffer sb = new StringBuffer();
                    if(province != null) {
                        item.setProvince(province.getName());
                        sb.append("/" + province.getName());
                    } else {
                        item.setProvince("");
                    }
                    TaskArea city = taskAreaMap.get(item.getCity());
                    if(city != null) {
                        item.setCity(city.getName());
                        sb.append("/" + city.getName());
                    } else {
                        item.setCity("");
                    }
                    TaskArea district = taskAreaMap.get(item.getDistrict());
                    if(district != null) {
                        item.setDistrict(district.getName());
                        sb.append("/" + district.getName());
                    } else {
                        item.setDistrict("");
                    }
                    if(sb.length() > 0) {
                        item.setArea(sb.substring(1));
                    }
                }
                String dept = deptMap.get(item.getDept());
                if(StringUtils.isBlank(dept)) {
                    dept = iUaDeptService.queryOrgNameById(item.getDept());
                    deptMap.put(item.getDept(), dept);
                }
                item.setDept(dept);
                item.setAnswerType(String.valueOf(CodeEnum.COMPUTER_EXAM.getCode()).equals(item.getAnswerType()) ? CodeEnum.COMPUTER_EXAM.getName() : CodeEnum.PAPER_EXAM.getName());
            });
            ProgressManager.updateProgressCurrent(30L, "数据准备完毕");
            // 集合总数据数
            int totalSize = dataList.size();
            EasyExcel.write(tempFile, ExportPlaceRoomDTO.class)
                    .registerWriteHandler(new AbstractRowWriteHandler() {
                        @Override
                        public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
                            super.afterRowDispose(writeSheetHolder, writeTableHolder, row, relativeRowIndex, isHead);
                            String result = StrUtil.format("正在生成文件，共计{}条数据，正在写入第{}条数据", totalSize, relativeRowIndex);
                            log.debug("# {}", result);
                            ProgressManager.stepProgressCurrent(relativeRowIndex, totalSize, 60L, 90L, result);
                        }
                    }).sheet("考点考场信息").doWrite(dataList);

            ProgressManager.updateProgressCurrent(90L, "文件开始生成");
            String path = "kw-gov/room/" + lastFileName;
            String fileUrl = cloudObsService.updateFile(path, tempFile);
            log.debug("# excel文件上传华为云成功：{}，{}", lastFileName, fileUrl);
            ProgressManager.updateProgressCurrent(99L, "文件生成成功");
            ProgressManager.updateProgressCurrent(100L, fileUrl);
        } finally {
            //tempFile.delete();
        }
    }

    @Override
    public ArrangeCount countRoom(String taskId) {
        return baseMapper.countRoom(taskId);
    }

    public static final String [] regex ={"[0-1]","[1-2]"};


    @Override
    public List<String> getTaskRoomByTaskId(String taskId) {
        List<TaskRoom> list = lambdaQuery()
                .eq(TaskRoom::getTaskId, taskId)
                .eq(TaskRoom::getDelFlag, false)
                .orderByAsc(TaskRoom::getCode)
                .list();
        List<String> roomIds = list.stream().filter(r -> Objects.nonNull(r.getPtRoomId()))
                .map(TaskRoom::getPtRoomId).collect(Collectors.toList());
        return roomIds;
    }

    @Override
    public List<TaskPlace> getTaskPlace(String taskId, int arrangeState) {
        return baseMapper.getTaskPlace(taskId, arrangeState);
    }

    @Override
    public List<com.hyt.it.ogt.kq.service.gov.model.vo.Tree> allTree(String taskId, String type) {
        List<TaskArea> allAreaList = iTaskAreaService.lambdaQuery().eq(TaskArea::getTaskId, taskId).eq(BaseEntity::getDelFlag, false).list();
        List<com.hyt.it.ogt.kq.service.gov.model.vo.Tree> disOrderTrees = allAreaList
                .stream()
                .map(area -> new com.hyt.it.ogt.kq.service.gov.model.vo.Tree()
                        .setId(area.getAreaId())
                        .setPid(area.getParentId())
                        .setText(area.getName()))
                .collect(Collectors.toList());
        return this.treeToMultilevel(disOrderTrees);
    }

    private   List<com.hyt.it.ogt.kq.service.gov.model.vo.Tree> treeToMultilevel(List<com.hyt.it.ogt.kq.service.gov.model.vo.Tree> trees) {
        Boolean loop = true;
        while (loop) {
            loop = false;
            for (int i = trees.size() - 1; i >= 0; i--) {
                String pid = trees.get(i).getPid();
                if (pid != null) {
                    String id = trees.get(i).getId();
                    int pIndex = -1;
                    Boolean noSon = true;
                    for (int j = 0; j < trees.size(); j++) {
                        if (trees.get(j).getPid() != null && trees.get(j).getPid().equals(id)) {
                            noSon = false;
                            break;
                        }
                        if (trees.get(j).getId().equals(pid)) {
                            pIndex = j;
                        }
                    }
                    if (noSon) {
                        if (pIndex >= 0) {
                            if (trees.get(pIndex).getChildren() == null) {
                                List<com.hyt.it.ogt.kq.service.gov.model.vo.Tree> children = new ArrayList<com.hyt.it.ogt.kq.service.gov.model.vo.Tree>();
                                trees.get(pIndex).setChildren(children);
                            }
                            trees.get(pIndex).getChildren().add(0, trees.get(i));
                            trees.remove(i);
                        }
                    } else {
                        loop = true;
                    }
                }
            }
        }
        return trees;
    }
}
